Our project sought to compare the quality of life in 26 major U.S. cities using data from the U.S. Centers for Disease Control and Prevention. The data set includes variables that pertain to demographics and indicators of health, income, and education level.
We intended to do this by producing interactive plots, making linear models of key predictors, and comparing cities based on their “Livability”, a variable we created representing relative levels of positive and negative predictors of quality of life.
Our hypothesis was that the “Livability” of Detroit, MI would be lowest because it is known for its weak economy, high levels of poverty, and high crime rates. Meanwhile, we predicted Boston, MA and Portland, OR would be relatively high on the list as they are known to be cities with thriving economies and rich local cultures.
The main dataset we used was the U.S. Centers for Disease Control and Prevention’s Big Cities Health Inventory Data. The R language and RStudio were used for all data wrangling, analyses, and visualizations. We researched previous work done to summarize the health, finances, and education levels of Americans. The Social Science Research Council has a comprehensive interactive map of the U.S. depicting a range of statistics regarding human wellbeing by state. Their map informed our approach to analysis and visualisation. Inspired by this resource, we decided presenting data on life expectancy, income, and education level by various demographics was crucial to summarizing quality of life in big cities.
References
R Core Team (2017). R: A language and environment for statistical computing. R Foundation for Statistical Computing,Vienna, Austria. URL https://www.R-project.org/.
RStudio Team (2015). RStudio: Integrated Development for R. RStudio, Inc., Boston, MA URL http://www.rstudio.com/.
Big Cities Health Data: https://data.world/health/big-cities-health
Measure of America Interactive Map: https://measureofamerica.org/maps/?state^hdi^all_all^HDI^hdi
Measure of America Data Set: http://measureofamerica.org/download-agreement/
First, our process involved finding a mean of the values for each city and indicator. Each city has multiple years of data for each indicator so obtaining a mean across these years is important for further analysis. We then calculated the sum of mean values for a given indicator across all cities. The city’s mean for each indicator was then divided by the sum of means in the indicator, we’ll call this value the city’s relative value. Since we have a relative value for every city in each indicator we can calculate totals of the relative value for the good and bad indicator categories. This is done by summing the relative values for the good indicators and for the bad indicators. Then to get a final ranking of the cities we subtracted the sum of the good indicators from the sum of the bad indicators for each city. The result decides the final ranking of the city. Finally, we plotted Livability by City.
After this, we made linear models to see if life expectancy and race and life expectancy and city had statistically significant associations.
Subsequently, we produced an interactive faceted barplot depicting life expectancy by city and race. Then, we made a series of static barplots showing median household income, all-cause mortality rate, and percent of adults who meet CDC recommended levels of physical activity by city in order to visualize the relative health and income levels of the cities. Finally, we produced three interesting barplots depicting firearms-related mortality rate, homicide rate, and suicide rate. It was surprising to find that Sacremento, CA has the highest suicide rate. It was unsurprising to find that Detroit, MI had both the highest homicide rate and the highest firearms-related mortality rate.
In conclusion, we found that the city that has the lowest quality of health, education, and income was Detroit, Michigan and the city with the best of these qualities was San Jose, California. One of the largest challenges in this project was finding a way to correctly quantify quality of life in a city. To get a reasonable result we had to try out a few different iterations of the final method we came to and we feel that this method is the most accurate way to rank these cities based on the data we had.
Detroit, MI matched our hypothesis, while Boston, MA and Portland, OR differed from it. One reason Boston, MA and Portland, OR were lower on the list than hypothesized was because the data set incorporated a limited number of predictors that do not fully encompass quality of life. Also, we did not weigh the variables differently in our calculation of “Livability”, when in reality some factors are more important than others.
Import data set into workspace; define vectors of variables representing variables unfit to be indicators of quality of life, and variables that are positive indicators of quality of life
library(ggplot2)
library(dplyr)
library(plotly)
#setwd("C:/Users/robin/OneDrive/Documents")
data <- read.csv("Big_Cities_Health_Data_Inventory.csv")
unrelated = c("Percent Foreign Born", "Race/Ethnicity", "Opioid-Related Mortality Rate (Age-Adjusted; Per 100,000 people) *These data should not be compared across cities as they have different definitions", "Opioid-Related Mortality Rate (Age-Adjusted; Per 100,000 people) *These data should not be compared across cities as they have different definitions", "Opioid-Related Mortality Rate (Crude Rate; Per 100,000 people) *These data should not be compared across cities as they have different definitions", "Opioid-Related Mortality Rate (Age-Adjusted; Per 100,000 people) *These data should not be compared across cities as they have different definitions.", "Drug Abuse-Related Hospitalization Rate (Per 100,000 people) *Comparisons of these data are difficult as definitions can vary.", "HIV Diagnoses Rate (Per 100,000 people)", "AIDS Diagnoses Rate (Per 100,000 people)", "All-Cause Mortality Rate (Age-Adjusted; Per 100,000 people)", "Percent of Adults Who Are Obese", "Percent of Adults Who Binge Drank", "Percent of Adults Who Currently Smoke", "Percent of High School Students Who Binge Drank", "Percent of High School Students Who Currently Smoke", "Rate of Laboratory Confirmed Infections Caused by Shiga Toxin-Producing E-Coli (Per 100,000 people)", "Life Expectancy at Birth (Years)", "Percent of Adults Over Age 65 Who Received Pneumonia Vaccine", "Percent of Adults Who Meet CDC-Recommended Physical Activity Levels", "Rate of Laboratory Confirmed Infections Caused by Salmonella (Per 100,000 people)", "Percent of High School Students Who Are Obese", "Percent of High School Students Who Meet CDC-Recommended Physical Activity Levels", "Percent of Adults Who Received Seasonal Flu Shot", "Percent of Children Who Received Seasonal Flu Shot", "Life Expectancy at Birth (Years)", "Percent of Adults Over Age 65 Who Received Pneumonia Vaccine", "Percent of Adults Who Meet CDC-Recommended Physical Activity Levels", "Rate of Laboratory Confirmed Infections Caused by Salmonella (Per 100,000 people)", "Percent of High School Students Who Are Obese", "Percent of High School Students Who Meet CDC-Recommended Physical Activity Levels", "Percent of Adults Who Received Seasonal Flu Shot", "Percent of Children Who Received Seasonal Flu Shot", "Life Expectancy at Birth (Years)", "Percent of Adults Over Age 65 Who Received Pneumonia Vaccine", "Percent of Adults Who Meet CDC-Recommended Physical Activity Levels", "Rate of Laboratory Confirmed Infections Caused by Salmonella (Per 100,000 people)", "Percent of High School Students Who Are Obese", "Percent of High School Students Who Meet CDC-Recommended Physical Activity Levels", "Percent of Adults Who Received Seasonal Flu Shot", "Percent of Children Who Received Seasonal Flu Shot")
good_indicators = c("Life Expectancy at Birth (Years)", "Median Household Income (Dollars)", "Percent 18+ High School Graduates", "Percent of Adults Over Age 65 Who Received Pneumonia Vaccine", "Percent of Adults Who Meet CDC-Recommended Physical Activity Levels", "Percent of High School Students Who Meet CDC-Recommended Physical Activity Levels", "Percent of Children Who Received Seasonal Flu Shot", "Percent of Adults Who Received Seasonal Flu Shot")
places <- unique(data$Place)
indicators <- unique(data$Indicator)
Create a variable “Mean_by_city_and_ind” equal to the mean of values for a given place and indicator
data <- data %>%
group_by(Indicator, Place) %>%
mutate(Mean_by_city_and_ind=mean(na.exclude(Value)))
data <- ungroup(data)
Create variable “Sum” equal to the sum of mean values for a given indicator across all places
data <- data %>%
group_by(Indicator) %>%
mutate(Sum=sum(unique(Mean_by_city_and_ind)))
data <- ungroup(data)
Create variable “Relative” equal to the quotient of the mean value for a given indicator and place divided by the sum of all mean values for that indicator across all places
data <- data %>%
mutate(Relative=Mean_by_city_and_ind/Sum)
Create a variable “Good” equal to the sum of the “Relative” values corresponding to good indicators for a given place
data <- data %>%
group_by(Place) %>%
mutate(Good=sum(unique(Relative[Indicator %in% good_indicators])))
data <- ungroup(data)
Create a variable “Bad” equal to the sum of the “Relative” values corresponding to a bad indicators for a given place
data <- data %>%
group_by(Place) %>%
mutate(Bad=sum(unique(Relative[!Indicator %in% good_indicators & !Indicator %in% unrelated])))
data<-ungroup(data)
Create a variable “Livability” equal to Good minus Bad values
data <- data %>%
mutate(Livability = Good-Bad)
Center Livability around 0
data$Livability <- scale(data$Livability)
Visualize livability by city
data1 <- data %>%
group_by(Place, Livability) %>%
summarize()
data1 <- ungroup(data1)
data1 <- data1[order(data1$Livability),]
ggplot(data1)+aes(reorder(Place, Livability), Livability)+geom_point(stat="identity")+theme(axis.text.x=element_text(angle=90, hjust=1, vjust=0.5))+labs(x="City")

Create a variable representing mean life expectancy by race and city
data2 <- data[data$Indicator == "Life Expectancy at Birth (Years)" & data$Race..Ethnicity != "All",]
data2 <- data2 %>%
group_by(Race..Ethnicity, Place) %>%
mutate(Mean_Life_Exp_by_Race_and_Place=mean(na.exclude(Value)))
data2 <- ungroup(data2)
life_exp.df<- summarise(group_by(data2, Race..Ethnicity, Place, Mean_Life_Exp_by_Race_and_Place))
life_exp.df.ordered <- life_exp.df[order(life_exp.df$Mean_Life_Exp_by_Race_and_Place),]
Linear model of life expectancy and city
summary(lm(Mean_Life_Exp_by_Race_and_Place ~ Place, life_exp.df))
Call:
lm(formula = Mean_Life_Exp_by_Race_and_Place ~ Place, data = life_exp.df)
Residuals:
Min 1Q Median 3Q Max
-10.225 -3.031 1.000 3.775 9.125
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 82.5250 2.6428 31.227 <2e-16 ***
PlaceCleveland, OH -3.2500 3.7375 -0.870 0.390
PlaceFort Worth (Tarrant County), TX -7.1250 4.5774 -1.557 0.128
PlaceKansas City, MO -6.3750 4.5774 -1.393 0.172
PlaceLas Vegas (Clark County), NV -0.9500 3.7375 -0.254 0.801
PlaceLong Beach, CA -1.5000 3.4118 -0.440 0.663
PlaceLos Angeles, CA -1.0250 3.7375 -0.274 0.785
PlaceNew York, NY -2.2917 4.0369 -0.568 0.574
PlaceOakland, CA -2.6917 4.0369 -0.667 0.509
PlacePortland (Multnomah County), OR -2.2050 3.5457 -0.622 0.538
PlaceSan Antonio, TX -4.1583 4.0369 -1.030 0.309
PlaceSan Diego County, CA 0.1667 3.7375 0.045 0.965
PlaceSan Francisco, CA -3.1000 3.7375 -0.829 0.412
PlaceSeattle, WA 1.1083 4.0369 0.275 0.785
PlaceWashington, DC -0.7583 4.0369 -0.188 0.852
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 5.286 on 39 degrees of freedom
Multiple R-squared: 0.1523, Adjusted R-squared: -0.152
F-statistic: 0.5006 on 14 and 39 DF, p-value: 0.9185
The results show that life expectancy is not correlated with city in a statistically significant way.
Linear model for mean life expectancy and race
summary(lm(Mean_Life_Exp_by_Race_and_Place ~ Race..Ethnicity, life_exp.df))
Call:
lm(formula = Mean_Life_Exp_by_Race_and_Place ~ Race..Ethnicity,
data = life_exp.df)
Residuals:
Min 1Q Median 3Q Max
-6.4956 -1.2864 0.0022 1.2381 6.8410
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 76.400 2.535 30.139 < 2e-16 ***
Race..EthnicityAsian/PI 10.071 2.689 3.746 0.000491 ***
Race..EthnicityBlack -1.323 2.618 -0.505 0.615595
Race..EthnicityHispanic 8.259 2.631 3.140 0.002923 **
Race..EthnicityMultiracial 3.100 3.585 0.865 0.391577
Race..EthnicityNative American 1.100 3.585 0.307 0.760320
Race..EthnicityWhite 3.496 2.618 1.335 0.188251
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.535 on 47 degrees of freedom
Multiple R-squared: 0.765, Adjusted R-squared: 0.735
F-statistic: 25.51 on 6 and 47 DF, p-value: 3.101e-13
The linear model shows that Asian and Hispanic races or ethnicities are have a statistically significant association with life expectancy. The resulting model is: life expectancy = 76.4 + 10.071 x Asian + 8.259 x Hispanic
Visualize mean life expectancy by race and city
gg <- ggplot(life_exp.df.ordered) + aes(x=reorder(Race..Ethnicity, Mean_Life_Exp_by_Race_and_Place) ,y=Mean_Life_Exp_by_Race_and_Place, fill = Race..Ethnicity) +
geom_bar(position = "dodge", stat="identity") + facet_wrap(~Place) + labs(title = "Life Expectancy by Race and City") + theme(axis.text.x=element_text(angle=90, hjust=1, vjust=0.5))
ggp_build <- plotly_build(gg)
ggp_build$layout$height = 800
ggp_build$layout$width = 600
ggp_build
Above is an interactive plot showing the mean life expectancy for cities by race.
Visualize median household income by city
income <- subset(data, Indicator == "Median Household Income (Dollars)", select = colnames(data))
income_val <- aggregate(Value ~ Place, income, mean)
ggplot(income_val, aes(x=reorder(Place, Value), y=Value)) + geom_col() + coord_flip() + labs(title = "Median Household Income (Dollars)", x="City")

San Jose, CA has the highest Median Household Income, while Detroit, MI has the lowest.
Visualize Mortality Rate by city and race
all_cause <- subset(data, Indicator == "All-Cause Mortality Rate (Age-Adjusted; Per 100,000 people)", select = colnames(data))
all_cause2 <- aggregate( Value ~ Place, all_cause, mean )
ggplot(all_cause2, aes(x=reorder(Place, Value), y=Value)) + geom_col() + coord_flip() + labs(title = "All-Cause Mortality Rate (Age-Adjusted; Per 100,000 people)", x="City")

Cleveland, OH has the lowest All-Cause Mortality Rate, while Phoenix, AZ has the lowest.
Visualize percent of adults who meet Physical Activity Requirements
pa <- subset(data, Indicator == "Percent of Adults Who Meet CDC-Recommended Physical Activity Levels", select = colnames(data))
pa_data <- aggregate( Value ~ Place, pa, mean)
ggplot(pa_data, aes(x=reorder(Place, Value), y=Value)) + geom_col() + coord_flip() + labs(title = "Percent of Adults Who Meet CDC-Recommended Physical Activity Levels", x="City")

Denver, CO has the highest percentage of adults who meet CDC-recommend physical activity levels, while Detroit, MI has the lowest.
Interesting plots
fa <- subset(data, Indicator == "Firearm Related Mortality Rate (Age-Adjusted; Per 100,000 people)", select = colnames(data))
fa_data <- aggregate( Value ~ Place, fa, mean)
ggplot(fa_data, aes(x=reorder(Place, Value), y=Value)) + geom_col() + coord_flip() + labs(title = "Firearm Related Mortality Rate (Age-Adjusted; Per 100,000 people)")

Detroit, MI has the highest firearm-related mortality rate, while New York, NY has the lowest.
hr <- subset(data, Indicator == "Homicide Rate (Age-Adjusted; Per 100,000 people)", select = colnames(data))
hr_data <- aggregate( Value ~ Place, hr, mean)
ggplot(hr_data, aes(x=reorder(Place, Value), y=Value)) + geom_col() + coord_flip() + labs(title = "Homicide Rate (Age-Adjusted; Per 100,000 people)", x="City")

Detroit, MI has the highest homicide rate, while San Diego, CA has the lowest
sr <- subset(data, Indicator == "Suicide Rate (Age-Adjusted; Per 100,000 people)", select = colnames(data))
sr_data <- aggregate( Value ~ Place, sr, mean)
ggplot(sr_data, aes(x=reorder(Place, Value), y=Value)) + geom_col() + coord_flip() + labs(title = "Suicide Rate (Age-Adjusted; Per 100,000 people)")

Sacramento, CA has the highest suicide rate, while Los Angeles, CA has the lowest.
LS0tDQp0aXRsZTogIlJhbmtpbmcgQ2l0aWVzIGJ5IEhlYWx0aCwgRWR1Y2F0aW9uLCBhbmQgSW5jb21lIg0KYXV0aG9yOiAiRHlsYW4gTWFydGluLCBSb2JpbiBMb3ZlbGwiDQpkYXRlOiAiTWF5IDNyZCwgMjAyMCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCglPdXIgcHJvamVjdCBzb3VnaHQgdG8gY29tcGFyZSB0aGUgcXVhbGl0eSBvZiBsaWZlIGluIDI2IG1ham9yIFUuUy4gY2l0aWVzIHVzaW5nIGRhdGEgZnJvbSB0aGUgVS5TLiBDZW50ZXJzIGZvciBEaXNlYXNlIENvbnRyb2wgYW5kIFByZXZlbnRpb24uIFRoZSBkYXRhIHNldCBpbmNsdWRlcyB2YXJpYWJsZXMgdGhhdCBwZXJ0YWluIHRvIGRlbW9ncmFwaGljcyBhbmQgaW5kaWNhdG9ycyBvZiBoZWFsdGgsIGluY29tZSwgYW5kIGVkdWNhdGlvbiBsZXZlbC4gDQoJDQoJV2UgaW50ZW5kZWQgdG8gZG8gdGhpcyBieSBwcm9kdWNpbmcgaW50ZXJhY3RpdmUgcGxvdHMsIG1ha2luZyBsaW5lYXIgbW9kZWxzIG9mIGtleSBwcmVkaWN0b3JzLCBhbmQgY29tcGFyaW5nIGNpdGllcyBiYXNlZCBvbiB0aGVpciDigJxMaXZhYmlsaXR54oCdLCBhIHZhcmlhYmxlIHdlIGNyZWF0ZWQgcmVwcmVzZW50aW5nIHJlbGF0aXZlIGxldmVscyBvZiBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgcHJlZGljdG9ycyBvZiBxdWFsaXR5IG9mIGxpZmUuIA0KICANCiAgT3VyIGh5cG90aGVzaXMgd2FzIHRoYXQgdGhlIOKAnExpdmFiaWxpdHnigJ0gb2YgRGV0cm9pdCwgTUkgd291bGQgYmUgbG93ZXN0IGJlY2F1c2UgaXQgaXMga25vd24gZm9yIGl0cyB3ZWFrIGVjb25vbXksIGhpZ2ggbGV2ZWxzIG9mIHBvdmVydHksIGFuZCBoaWdoIGNyaW1lIHJhdGVzLiBNZWFud2hpbGUsIHdlIHByZWRpY3RlZCBCb3N0b24sIE1BIGFuZCBQb3J0bGFuZCwgT1Igd291bGQgYmUgcmVsYXRpdmVseSBoaWdoIG9uIHRoZSBsaXN0IGFzIHRoZXkgYXJlIGtub3duIHRvIGJlIGNpdGllcyB3aXRoIHRocml2aW5nIGVjb25vbWllcyBhbmQgcmljaCBsb2NhbCBjdWx0dXJlcy4NCgkNCglUaGUgbWFpbiBkYXRhc2V0IHdlIHVzZWQgd2FzIHRoZSBVLlMuIENlbnRlcnMgZm9yIERpc2Vhc2UgQ29udHJvbCBhbmQgUHJldmVudGlvbuKAmXMgQmlnIENpdGllcyBIZWFsdGggSW52ZW50b3J5IERhdGEuIFRoZSBSIGxhbmd1YWdlIGFuZCBSU3R1ZGlvIHdlcmUgdXNlZCBmb3IgYWxsIGRhdGEgd3JhbmdsaW5nLCBhbmFseXNlcywgYW5kIHZpc3VhbGl6YXRpb25zLiBXZSByZXNlYXJjaGVkIHByZXZpb3VzIHdvcmsgZG9uZSB0byBzdW1tYXJpemUgdGhlIGhlYWx0aCwgZmluYW5jZXMsIGFuZCBlZHVjYXRpb24gbGV2ZWxzIG9mIEFtZXJpY2Fucy4gVGhlIFNvY2lhbCBTY2llbmNlIFJlc2VhcmNoIENvdW5jaWwgaGFzIGEgY29tcHJlaGVuc2l2ZSBpbnRlcmFjdGl2ZSBtYXAgb2YgdGhlIFUuUy4gZGVwaWN0aW5nIGEgcmFuZ2Ugb2Ygc3RhdGlzdGljcyByZWdhcmRpbmcgaHVtYW4gd2VsbGJlaW5nIGJ5IHN0YXRlLiBUaGVpciBtYXAgaW5mb3JtZWQgb3VyIGFwcHJvYWNoIHRvIGFuYWx5c2lzIGFuZCB2aXN1YWxpc2F0aW9uLiBJbnNwaXJlZCBieSB0aGlzIHJlc291cmNlLCB3ZSBkZWNpZGVkIHByZXNlbnRpbmcgZGF0YSBvbiBsaWZlIGV4cGVjdGFuY3ksIGluY29tZSwgYW5kIGVkdWNhdGlvbiBsZXZlbCBieSB2YXJpb3VzIGRlbW9ncmFwaGljcyB3YXMgY3J1Y2lhbCB0byBzdW1tYXJpemluZyBxdWFsaXR5IG9mIGxpZmUgaW4gYmlnIGNpdGllcy4gDQoNClJlZmVyZW5jZXMNCg0KUiBDb3JlIFRlYW0gKDIwMTcpLiBSOiBBIGxhbmd1YWdlIGFuZCBlbnZpcm9ubWVudCBmb3Igc3RhdGlzdGljYWwgY29tcHV0aW5nLiBSIEZvdW5kYXRpb24gZm9yIFN0YXRpc3RpY2FsIA0KICBDb21wdXRpbmcsVmllbm5hLCBBdXN0cmlhLiBVUkwgaHR0cHM6Ly93d3cuUi1wcm9qZWN0Lm9yZy8uDQoNClJTdHVkaW8gVGVhbSAoMjAxNSkuIFJTdHVkaW86IEludGVncmF0ZWQgRGV2ZWxvcG1lbnQgZm9yIFIuIFJTdHVkaW8sIEluYy4sIEJvc3RvbiwgTUEgVVJMIGh0dHA6Ly93d3cucnN0dWRpby5jb20vLg0KDQpCaWcgQ2l0aWVzIEhlYWx0aCBEYXRhOiANCmh0dHBzOi8vZGF0YS53b3JsZC9oZWFsdGgvYmlnLWNpdGllcy1oZWFsdGgNCg0KTWVhc3VyZSBvZiBBbWVyaWNhIEludGVyYWN0aXZlIE1hcDoNCmh0dHBzOi8vbWVhc3VyZW9mYW1lcmljYS5vcmcvbWFwcy8/c3RhdGVeaGRpXmFsbF9hbGxeSERJXmhkaQ0KDQpNZWFzdXJlIG9mIEFtZXJpY2EgRGF0YSBTZXQ6DQpodHRwOi8vbWVhc3VyZW9mYW1lcmljYS5vcmcvZG93bmxvYWQtYWdyZWVtZW50Lw0KDQogIEZpcnN0LCBvdXIgcHJvY2VzcyBpbnZvbHZlZCBmaW5kaW5nIGEgbWVhbiBvZiB0aGUgdmFsdWVzIGZvciBlYWNoIGNpdHkgYW5kIGluZGljYXRvci4gRWFjaCBjaXR5IGhhcyBtdWx0aXBsZSB5ZWFycyBvZiBkYXRhIGZvciBlYWNoIGluZGljYXRvciBzbyBvYnRhaW5pbmcgYSBtZWFuIGFjcm9zcyB0aGVzZSB5ZWFycyBpcyBpbXBvcnRhbnQgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuIFdlIHRoZW4gY2FsY3VsYXRlZCB0aGUgc3VtIG9mIG1lYW4gdmFsdWVzIGZvciBhIGdpdmVuIGluZGljYXRvciBhY3Jvc3MgYWxsIGNpdGllcy4gVGhlIGNpdHnigJlzIG1lYW4gZm9yIGVhY2ggaW5kaWNhdG9yIHdhcyB0aGVuIGRpdmlkZWQgYnkgdGhlIHN1bSBvZiBtZWFucyBpbiB0aGUgaW5kaWNhdG9yLCB3ZeKAmWxsIGNhbGwgdGhpcyB2YWx1ZSB0aGUgY2l0eeKAmXMgcmVsYXRpdmUgdmFsdWUuIFNpbmNlIHdlIGhhdmUgYSByZWxhdGl2ZSB2YWx1ZSBmb3IgZXZlcnkgY2l0eSBpbiBlYWNoIGluZGljYXRvciB3ZSBjYW4gY2FsY3VsYXRlIHRvdGFscyBvZiB0aGUgcmVsYXRpdmUgdmFsdWUgZm9yIHRoZSBnb29kIGFuZCBiYWQgaW5kaWNhdG9yIGNhdGVnb3JpZXMuIFRoaXMgaXMgZG9uZSBieSBzdW1taW5nIHRoZSByZWxhdGl2ZSB2YWx1ZXMgZm9yIHRoZSBnb29kIGluZGljYXRvcnMgYW5kIGZvciB0aGUgYmFkIGluZGljYXRvcnMuIFRoZW4gdG8gZ2V0IGEgZmluYWwgcmFua2luZyBvZiB0aGUgY2l0aWVzIHdlIHN1YnRyYWN0ZWQgdGhlIHN1bSBvZiB0aGUgZ29vZCBpbmRpY2F0b3JzIGZyb20gdGhlIHN1bSBvZiB0aGUgYmFkIGluZGljYXRvcnMgZm9yIGVhY2ggY2l0eS4gVGhlIHJlc3VsdCBkZWNpZGVzIHRoZSBmaW5hbCByYW5raW5nIG9mIHRoZSBjaXR5LiBGaW5hbGx5LCB3ZSBwbG90dGVkIExpdmFiaWxpdHkgYnkgQ2l0eS4NCiAgDQogIEFmdGVyIHRoaXMsIHdlIG1hZGUgbGluZWFyIG1vZGVscyB0byBzZWUgaWYgbGlmZSBleHBlY3RhbmN5IGFuZCByYWNlIGFuZCBsaWZlIGV4cGVjdGFuY3kgYW5kIGNpdHkgaGFkIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXNzb2NpYXRpb25zLiANCiAgDQogIFN1YnNlcXVlbnRseSwgd2UgcHJvZHVjZWQgYW4gaW50ZXJhY3RpdmUgZmFjZXRlZCBiYXJwbG90IGRlcGljdGluZyBsaWZlIGV4cGVjdGFuY3kgYnkgY2l0eSBhbmQgcmFjZS4gVGhlbiwgd2UgbWFkZSBhIHNlcmllcyBvZiBzdGF0aWMgYmFycGxvdHMgc2hvd2luZyBtZWRpYW4gaG91c2Vob2xkIGluY29tZSwgYWxsLWNhdXNlIG1vcnRhbGl0eSByYXRlLCBhbmQgcGVyY2VudCBvZiBhZHVsdHMgd2hvIG1lZXQgQ0RDIHJlY29tbWVuZGVkIGxldmVscyBvZiBwaHlzaWNhbCBhY3Rpdml0eSBieSBjaXR5IGluIG9yZGVyIHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpdmUgaGVhbHRoIGFuZCBpbmNvbWUgbGV2ZWxzIG9mIHRoZSBjaXRpZXMuIEZpbmFsbHksIHdlIHByb2R1Y2VkIHRocmVlIGludGVyZXN0aW5nIGJhcnBsb3RzIGRlcGljdGluZyBmaXJlYXJtcy1yZWxhdGVkIG1vcnRhbGl0eSByYXRlLCBob21pY2lkZSByYXRlLCBhbmQgc3VpY2lkZSByYXRlLiBJdCB3YXMgc3VycHJpc2luZyB0byBmaW5kIHRoYXQgU2FjcmVtZW50bywgQ0EgaGFzIHRoZSBoaWdoZXN0IHN1aWNpZGUgcmF0ZS4gSXQgd2FzIHVuc3VycHJpc2luZyB0byBmaW5kIHRoYXQgRGV0cm9pdCwgTUkgaGFkIGJvdGggdGhlIGhpZ2hlc3QgaG9taWNpZGUgcmF0ZSBhbmQgdGhlIGhpZ2hlc3QgZmlyZWFybXMtcmVsYXRlZCBtb3J0YWxpdHkgcmF0ZS4NCiAgDQogIEluIGNvbmNsdXNpb24sIHdlIGZvdW5kIHRoYXQgdGhlIGNpdHkgdGhhdCBoYXMgdGhlIGxvd2VzdCBxdWFsaXR5IG9mIGhlYWx0aCwgZWR1Y2F0aW9uLCBhbmQgaW5jb21lIHdhcyBEZXRyb2l0LCBNaWNoaWdhbiBhbmQgdGhlIGNpdHkgd2l0aCB0aGUgYmVzdCBvZiB0aGVzZSBxdWFsaXRpZXMgd2FzIFNhbiBKb3NlLCBDYWxpZm9ybmlhLiBPbmUgb2YgdGhlIGxhcmdlc3QgY2hhbGxlbmdlcyBpbiB0aGlzIHByb2plY3Qgd2FzIGZpbmRpbmcgYSB3YXkgdG8gY29ycmVjdGx5IHF1YW50aWZ5IHF1YWxpdHkgb2YgbGlmZSBpbiBhIGNpdHkuIFRvIGdldCBhIHJlYXNvbmFibGUgcmVzdWx0IHdlIGhhZCB0byB0cnkgb3V0IGEgZmV3IGRpZmZlcmVudCBpdGVyYXRpb25zIG9mIHRoZSBmaW5hbCBtZXRob2Qgd2UgY2FtZSB0byBhbmQgd2UgZmVlbCB0aGF0IHRoaXMgbWV0aG9kIGlzIHRoZSBtb3N0IGFjY3VyYXRlIHdheSB0byByYW5rIHRoZXNlIGNpdGllcyBiYXNlZCBvbiB0aGUgZGF0YSB3ZSBoYWQuIA0KICANCiAgRGV0cm9pdCwgTUkgbWF0Y2hlZCBvdXIgaHlwb3RoZXNpcywgd2hpbGUgQm9zdG9uLCBNQSBhbmQgUG9ydGxhbmQsIE9SIGRpZmZlcmVkIGZyb20gaXQuIE9uZSByZWFzb24gQm9zdG9uLCBNQSBhbmQgUG9ydGxhbmQsIE9SIHdlcmUgbG93ZXIgb24gdGhlIGxpc3QgdGhhbiBoeXBvdGhlc2l6ZWQgd2FzIGJlY2F1c2UgdGhlIGRhdGEgc2V0IGluY29ycG9yYXRlZCBhIGxpbWl0ZWQgbnVtYmVyIG9mIHByZWRpY3RvcnMgdGhhdCBkbyBub3QgZnVsbHkgZW5jb21wYXNzIHF1YWxpdHkgb2YgbGlmZS4gQWxzbywgd2UgZGlkIG5vdCB3ZWlnaCB0aGUgdmFyaWFibGVzIGRpZmZlcmVudGx5IGluIG91ciBjYWxjdWxhdGlvbiBvZiDigJxMaXZhYmlsaXR54oCdLCB3aGVuIGluIHJlYWxpdHkgc29tZSBmYWN0b3JzIGFyZSBtb3JlIGltcG9ydGFudCB0aGFuIG90aGVycy4NCg0KDQoNCg0KSW1wb3J0IGRhdGEgc2V0IGludG8gd29ya3NwYWNlOyBkZWZpbmUgdmVjdG9ycyBvZiB2YXJpYWJsZXMgcmVwcmVzZW50aW5nIHZhcmlhYmxlcyB1bmZpdCB0byBiZSBpbmRpY2F0b3JzIG9mIHF1YWxpdHkgb2YgbGlmZSwgYW5kIHZhcmlhYmxlcyB0aGF0IGFyZSBwb3NpdGl2ZSBpbmRpY2F0b3JzIG9mIHF1YWxpdHkgb2YgbGlmZQ0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHBsb3RseSkNCiNzZXR3ZCgiQzovVXNlcnMvcm9iaW4vT25lRHJpdmUvRG9jdW1lbnRzIikNCmRhdGEgPC0gcmVhZC5jc3YoIkJpZ19DaXRpZXNfSGVhbHRoX0RhdGFfSW52ZW50b3J5LmNzdiIpDQoNCg0KdW5yZWxhdGVkID0gYygiUGVyY2VudCBGb3JlaWduIEJvcm4iLCAiUmFjZS9FdGhuaWNpdHkiLCAiT3Bpb2lkLVJlbGF0ZWQgTW9ydGFsaXR5IFJhdGUgKEFnZS1BZGp1c3RlZDsgUGVyIDEwMCwwMDAgcGVvcGxlKSAqVGhlc2UgZGF0YSBzaG91bGQgbm90IGJlIGNvbXBhcmVkIGFjcm9zcyBjaXRpZXMgYXMgdGhleSBoYXZlIGRpZmZlcmVudCBkZWZpbml0aW9ucyIsICJPcGlvaWQtUmVsYXRlZCBNb3J0YWxpdHkgUmF0ZSAoQWdlLUFkanVzdGVkOyBQZXIgMTAwLDAwMCBwZW9wbGUpICpUaGVzZSBkYXRhIHNob3VsZCBub3QgYmUgY29tcGFyZWQgYWNyb3NzIGNpdGllcyBhcyB0aGV5IGhhdmUgZGlmZmVyZW50IGRlZmluaXRpb25zIiwgIk9waW9pZC1SZWxhdGVkIE1vcnRhbGl0eSBSYXRlIChDcnVkZSBSYXRlOyBQZXIgMTAwLDAwMCBwZW9wbGUpICpUaGVzZSBkYXRhIHNob3VsZCBub3QgYmUgY29tcGFyZWQgYWNyb3NzIGNpdGllcyBhcyB0aGV5IGhhdmUgZGlmZmVyZW50IGRlZmluaXRpb25zIiwgIk9waW9pZC1SZWxhdGVkIE1vcnRhbGl0eSBSYXRlIChBZ2UtQWRqdXN0ZWQ7IFBlciAxMDAsMDAwIHBlb3BsZSkgKlRoZXNlIGRhdGEgc2hvdWxkIG5vdCBiZSBjb21wYXJlZCBhY3Jvc3MgY2l0aWVzIGFzIHRoZXkgaGF2ZSBkaWZmZXJlbnQgZGVmaW5pdGlvbnMuIiwgIkRydWcgQWJ1c2UtUmVsYXRlZCBIb3NwaXRhbGl6YXRpb24gUmF0ZSAoUGVyIDEwMCwwMDAgcGVvcGxlKSAqQ29tcGFyaXNvbnMgb2YgdGhlc2UgZGF0YSBhcmUgZGlmZmljdWx0IGFzIGRlZmluaXRpb25zIGNhbiB2YXJ5LiIsICJISVYgRGlhZ25vc2VzIFJhdGUgKFBlciAxMDAsMDAwIHBlb3BsZSkiLCAiQUlEUyBEaWFnbm9zZXMgUmF0ZSAoUGVyIDEwMCwwMDAgcGVvcGxlKSIsICJBbGwtQ2F1c2UgTW9ydGFsaXR5IFJhdGUgKEFnZS1BZGp1c3RlZDsgUGVyIDEwMCwwMDAgcGVvcGxlKSIsICJQZXJjZW50IG9mIEFkdWx0cyBXaG8gQXJlIE9iZXNlIiwgIlBlcmNlbnQgb2YgQWR1bHRzIFdobyBCaW5nZSBEcmFuayIsICJQZXJjZW50IG9mIEFkdWx0cyBXaG8gQ3VycmVudGx5IFNtb2tlIiwgIlBlcmNlbnQgb2YgSGlnaCBTY2hvb2wgU3R1ZGVudHMgV2hvIEJpbmdlIERyYW5rIiwgIlBlcmNlbnQgb2YgSGlnaCBTY2hvb2wgU3R1ZGVudHMgV2hvIEN1cnJlbnRseSBTbW9rZSIsICJSYXRlIG9mIExhYm9yYXRvcnkgQ29uZmlybWVkIEluZmVjdGlvbnMgQ2F1c2VkIGJ5IFNoaWdhIFRveGluLVByb2R1Y2luZyBFLUNvbGkgKFBlciAxMDAsMDAwIHBlb3BsZSkiLCAiTGlmZSBFeHBlY3RhbmN5IGF0IEJpcnRoIChZZWFycykiLCAiUGVyY2VudCBvZiBBZHVsdHMgT3ZlciBBZ2UgNjUgV2hvIFJlY2VpdmVkIFBuZXVtb25pYSBWYWNjaW5lIiwgIlBlcmNlbnQgb2YgQWR1bHRzIFdobyBNZWV0IENEQy1SZWNvbW1lbmRlZCBQaHlzaWNhbCBBY3Rpdml0eSBMZXZlbHMiLCAiUmF0ZSBvZiBMYWJvcmF0b3J5IENvbmZpcm1lZCBJbmZlY3Rpb25zIENhdXNlZCBieSBTYWxtb25lbGxhIChQZXIgMTAwLDAwMCBwZW9wbGUpIiwgIlBlcmNlbnQgb2YgSGlnaCBTY2hvb2wgU3R1ZGVudHMgV2hvIEFyZSBPYmVzZSIsICJQZXJjZW50IG9mIEhpZ2ggU2Nob29sIFN0dWRlbnRzIFdobyBNZWV0IENEQy1SZWNvbW1lbmRlZCBQaHlzaWNhbCBBY3Rpdml0eSBMZXZlbHMiLCAiUGVyY2VudCBvZiBBZHVsdHMgV2hvIFJlY2VpdmVkIFNlYXNvbmFsIEZsdSBTaG90IiwgIlBlcmNlbnQgb2YgQ2hpbGRyZW4gV2hvIFJlY2VpdmVkIFNlYXNvbmFsIEZsdSBTaG90IiwgIkxpZmUgRXhwZWN0YW5jeSBhdCBCaXJ0aCAoWWVhcnMpIiwgIlBlcmNlbnQgb2YgQWR1bHRzIE92ZXIgQWdlIDY1IFdobyBSZWNlaXZlZCBQbmV1bW9uaWEgVmFjY2luZSIsICJQZXJjZW50IG9mIEFkdWx0cyBXaG8gTWVldCBDREMtUmVjb21tZW5kZWQgUGh5c2ljYWwgQWN0aXZpdHkgTGV2ZWxzIiwgIlJhdGUgb2YgTGFib3JhdG9yeSBDb25maXJtZWQgSW5mZWN0aW9ucyBDYXVzZWQgYnkgU2FsbW9uZWxsYSAoUGVyIDEwMCwwMDAgcGVvcGxlKSIsICJQZXJjZW50IG9mIEhpZ2ggU2Nob29sIFN0dWRlbnRzIFdobyBBcmUgT2Jlc2UiLCAiUGVyY2VudCBvZiBIaWdoIFNjaG9vbCBTdHVkZW50cyBXaG8gTWVldCBDREMtUmVjb21tZW5kZWQgUGh5c2ljYWwgQWN0aXZpdHkgTGV2ZWxzIiwgIlBlcmNlbnQgb2YgQWR1bHRzIFdobyBSZWNlaXZlZCBTZWFzb25hbCBGbHUgU2hvdCIsICJQZXJjZW50IG9mIENoaWxkcmVuIFdobyBSZWNlaXZlZCBTZWFzb25hbCBGbHUgU2hvdCIsICJMaWZlIEV4cGVjdGFuY3kgYXQgQmlydGggKFllYXJzKSIsICJQZXJjZW50IG9mIEFkdWx0cyBPdmVyIEFnZSA2NSBXaG8gUmVjZWl2ZWQgUG5ldW1vbmlhIFZhY2NpbmUiLCAiUGVyY2VudCBvZiBBZHVsdHMgV2hvIE1lZXQgQ0RDLVJlY29tbWVuZGVkIFBoeXNpY2FsIEFjdGl2aXR5IExldmVscyIsICJSYXRlIG9mIExhYm9yYXRvcnkgQ29uZmlybWVkIEluZmVjdGlvbnMgQ2F1c2VkIGJ5IFNhbG1vbmVsbGEgKFBlciAxMDAsMDAwIHBlb3BsZSkiLCAiUGVyY2VudCBvZiBIaWdoIFNjaG9vbCBTdHVkZW50cyBXaG8gQXJlIE9iZXNlIiwgIlBlcmNlbnQgb2YgSGlnaCBTY2hvb2wgU3R1ZGVudHMgV2hvIE1lZXQgQ0RDLVJlY29tbWVuZGVkIFBoeXNpY2FsIEFjdGl2aXR5IExldmVscyIsICJQZXJjZW50IG9mIEFkdWx0cyBXaG8gUmVjZWl2ZWQgU2Vhc29uYWwgRmx1IFNob3QiLCAiUGVyY2VudCBvZiBDaGlsZHJlbiBXaG8gUmVjZWl2ZWQgU2Vhc29uYWwgRmx1IFNob3QiKQ0KDQpnb29kX2luZGljYXRvcnMgPSBjKCJMaWZlIEV4cGVjdGFuY3kgYXQgQmlydGggKFllYXJzKSIsICJNZWRpYW4gSG91c2Vob2xkIEluY29tZSAoRG9sbGFycykiLCAiUGVyY2VudCAxOCsgSGlnaCBTY2hvb2wgR3JhZHVhdGVzIiwgIlBlcmNlbnQgb2YgQWR1bHRzIE92ZXIgQWdlIDY1IFdobyBSZWNlaXZlZCBQbmV1bW9uaWEgVmFjY2luZSIsICJQZXJjZW50IG9mIEFkdWx0cyBXaG8gTWVldCBDREMtUmVjb21tZW5kZWQgUGh5c2ljYWwgQWN0aXZpdHkgTGV2ZWxzIiwgIlBlcmNlbnQgb2YgSGlnaCBTY2hvb2wgU3R1ZGVudHMgV2hvIE1lZXQgQ0RDLVJlY29tbWVuZGVkIFBoeXNpY2FsIEFjdGl2aXR5IExldmVscyIsICJQZXJjZW50IG9mIENoaWxkcmVuIFdobyBSZWNlaXZlZCBTZWFzb25hbCBGbHUgU2hvdCIsICJQZXJjZW50IG9mIEFkdWx0cyBXaG8gUmVjZWl2ZWQgU2Vhc29uYWwgRmx1IFNob3QiKQ0KDQpwbGFjZXMgPC0gdW5pcXVlKGRhdGEkUGxhY2UpDQppbmRpY2F0b3JzIDwtIHVuaXF1ZShkYXRhJEluZGljYXRvcikNCg0KYGBgDQoNCg0KQ3JlYXRlIGEgdmFyaWFibGUgIk1lYW5fYnlfY2l0eV9hbmRfaW5kIiBlcXVhbCB0byB0aGUgbWVhbiBvZiB2YWx1ZXMgZm9yIGEgZ2l2ZW4gcGxhY2UgYW5kIGluZGljYXRvcg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCmRhdGEgPC0gZGF0YSAlPiUgDQogIGdyb3VwX2J5KEluZGljYXRvciwgUGxhY2UpICU+JSANCiAgbXV0YXRlKE1lYW5fYnlfY2l0eV9hbmRfaW5kPW1lYW4obmEuZXhjbHVkZShWYWx1ZSkpKQ0KZGF0YSA8LSB1bmdyb3VwKGRhdGEpDQpgYGANCg0KQ3JlYXRlIHZhcmlhYmxlICJTdW0iIGVxdWFsIHRvIHRoZSBzdW0gb2YgbWVhbiB2YWx1ZXMgZm9yIGEgZ2l2ZW4gaW5kaWNhdG9yIGFjcm9zcyBhbGwgcGxhY2VzDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgZXJyb3I9RiwgbWVzc2FnZT1GfQ0KZGF0YSA8LSBkYXRhICU+JSANCiAgZ3JvdXBfYnkoSW5kaWNhdG9yKSAlPiUgDQogIG11dGF0ZShTdW09c3VtKHVuaXF1ZShNZWFuX2J5X2NpdHlfYW5kX2luZCkpKQ0KZGF0YSA8LSB1bmdyb3VwKGRhdGEpDQpgYGANCg0KQ3JlYXRlIHZhcmlhYmxlICJSZWxhdGl2ZSIgZXF1YWwgdG8gdGhlIHF1b3RpZW50IG9mIHRoZSBtZWFuIHZhbHVlIGZvciBhIGdpdmVuIGluZGljYXRvciBhbmQgcGxhY2UgZGl2aWRlZCBieSB0aGUgc3VtIG9mIGFsbCBtZWFuIHZhbHVlcyBmb3IgdGhhdCBpbmRpY2F0b3IgYWNyb3NzIGFsbCBwbGFjZXMNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GLCBtZXNzYWdlPUZ9DQpkYXRhIDwtIGRhdGEgJT4lIA0KICBtdXRhdGUoUmVsYXRpdmU9TWVhbl9ieV9jaXR5X2FuZF9pbmQvU3VtKQ0KYGBgDQoNCkNyZWF0ZSBhIHZhcmlhYmxlICJHb29kIiBlcXVhbCB0byB0aGUgc3VtIG9mIHRoZSAiUmVsYXRpdmUiIHZhbHVlcyBjb3JyZXNwb25kaW5nIHRvIGdvb2QgaW5kaWNhdG9ycyBmb3IgYSBnaXZlbiBwbGFjZQ0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCmRhdGEgPC0gZGF0YSAlPiUgDQogIGdyb3VwX2J5KFBsYWNlKSAlPiUgDQogIG11dGF0ZShHb29kPXN1bSh1bmlxdWUoUmVsYXRpdmVbSW5kaWNhdG9yICVpbiUgZ29vZF9pbmRpY2F0b3JzXSkpKQ0KDQpkYXRhIDwtIHVuZ3JvdXAoZGF0YSkNCmBgYA0KDQpDcmVhdGUgYSB2YXJpYWJsZSAiQmFkIiBlcXVhbCB0byB0aGUgc3VtIG9mIHRoZSAiUmVsYXRpdmUiIHZhbHVlcyBjb3JyZXNwb25kaW5nIHRvIGEgYmFkIGluZGljYXRvcnMgZm9yIGEgZ2l2ZW4gcGxhY2UNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GLCBtZXNzYWdlPUZ9DQpkYXRhIDwtIGRhdGEgJT4lIA0KICBncm91cF9ieShQbGFjZSkgJT4lIA0KICBtdXRhdGUoQmFkPXN1bSh1bmlxdWUoUmVsYXRpdmVbIUluZGljYXRvciAlaW4lIGdvb2RfaW5kaWNhdG9ycyAmICFJbmRpY2F0b3IgJWluJSB1bnJlbGF0ZWRdKSkpDQpkYXRhPC11bmdyb3VwKGRhdGEpDQpgYGANCg0KQ3JlYXRlIGEgdmFyaWFibGUgIkxpdmFiaWxpdHkiIGVxdWFsIHRvIEdvb2QgbWludXMgQmFkIHZhbHVlcw0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKExpdmFiaWxpdHkgPSBHb29kLUJhZCkNCmBgYA0KQ2VudGVyIExpdmFiaWxpdHkgYXJvdW5kIDANCmBgYHtyLCB3YXJuaW5nPUYsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCmRhdGEkTGl2YWJpbGl0eSA8LSBzY2FsZShkYXRhJExpdmFiaWxpdHkpDQoNCmBgYA0KDQpWaXN1YWxpemUgbGl2YWJpbGl0eSBieSBjaXR5DQpgYGB7ciwgd2FybmluZz1GQUxTRSwgZXJyb3I9RiwgbWVzc2FnZT1GfQ0KZGF0YTEgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoUGxhY2UsIExpdmFiaWxpdHkpICU+JSANCiAgc3VtbWFyaXplKCkNCmRhdGExIDwtIHVuZ3JvdXAoZGF0YTEpDQpkYXRhMSA8LSBkYXRhMVtvcmRlcihkYXRhMSRMaXZhYmlsaXR5KSxdDQpnZ3Bsb3QoZGF0YTEpK2FlcyhyZW9yZGVyKFBsYWNlLCBMaXZhYmlsaXR5KSwgTGl2YWJpbGl0eSkrZ2VvbV9wb2ludChzdGF0PSJpZGVudGl0eSIpK3RoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSkrbGFicyh4PSJDaXR5IikNCmBgYA0KDQpDcmVhdGUgYSB2YXJpYWJsZSByZXByZXNlbnRpbmcgbWVhbiBsaWZlIGV4cGVjdGFuY3kgYnkgcmFjZSBhbmQgY2l0eQ0KYGBge3IsIHdhcm5pbmc9RiwgZXJyb3I9RiwgbWVzc2FnZT1GfQ0KZGF0YTIgPC0gZGF0YVtkYXRhJEluZGljYXRvciA9PSAiTGlmZSBFeHBlY3RhbmN5IGF0IEJpcnRoIChZZWFycykiICYgZGF0YSRSYWNlLi5FdGhuaWNpdHkgIT0gIkFsbCIsXQ0KZGF0YTIgPC0gZGF0YTIgJT4lIA0KICBncm91cF9ieShSYWNlLi5FdGhuaWNpdHksIFBsYWNlKSAlPiUgDQogIG11dGF0ZShNZWFuX0xpZmVfRXhwX2J5X1JhY2VfYW5kX1BsYWNlPW1lYW4obmEuZXhjbHVkZShWYWx1ZSkpKSANCmRhdGEyIDwtIHVuZ3JvdXAoZGF0YTIpDQoNCmxpZmVfZXhwLmRmPC0gc3VtbWFyaXNlKGdyb3VwX2J5KGRhdGEyLCBSYWNlLi5FdGhuaWNpdHksIFBsYWNlLCBNZWFuX0xpZmVfRXhwX2J5X1JhY2VfYW5kX1BsYWNlKSkNCmxpZmVfZXhwLmRmLm9yZGVyZWQgPC0gbGlmZV9leHAuZGZbb3JkZXIobGlmZV9leHAuZGYkTWVhbl9MaWZlX0V4cF9ieV9SYWNlX2FuZF9QbGFjZSksXQ0KDQpgYGANCg0KTGluZWFyIG1vZGVsIG9mIGxpZmUgZXhwZWN0YW5jeSBhbmQgY2l0eQ0KYGBge3IsIHdhcm5pbmc9RiwgZXJyb3I9RiwgbWVzc2FnZT1GfQ0Kc3VtbWFyeShsbShNZWFuX0xpZmVfRXhwX2J5X1JhY2VfYW5kX1BsYWNlIH4gUGxhY2UsIGxpZmVfZXhwLmRmKSkNCmBgYA0KPg0KVGhlIHJlc3VsdHMgc2hvdyB0aGF0IGxpZmUgZXhwZWN0YW5jeSBpcyBub3QgY29ycmVsYXRlZCB3aXRoIGNpdHkgaW4gYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHdheS4NCg0KTGluZWFyIG1vZGVsIGZvciBtZWFuIGxpZmUgZXhwZWN0YW5jeSBhbmQgcmFjZQ0KDQpgYGB7ciwgd2FybmluZz1GLCBlcnJvcj1GLCBtZXNzYWdlPUZ9DQpzdW1tYXJ5KGxtKE1lYW5fTGlmZV9FeHBfYnlfUmFjZV9hbmRfUGxhY2UgfiBSYWNlLi5FdGhuaWNpdHksIGxpZmVfZXhwLmRmKSkNCmBgYA0KDQo+DQpUaGUgbGluZWFyIG1vZGVsIHNob3dzIHRoYXQgQXNpYW4gYW5kIEhpc3BhbmljIHJhY2VzIG9yIGV0aG5pY2l0aWVzIGFyZSBoYXZlIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhc3NvY2lhdGlvbiB3aXRoIGxpZmUgZXhwZWN0YW5jeS4NClRoZSByZXN1bHRpbmcgbW9kZWwgaXM6DQpsaWZlIGV4cGVjdGFuY3kgPSA3Ni40ICsgMTAuMDcxIHggQXNpYW4gKyA4LjI1OSB4IEhpc3BhbmljDQoNCg0KVmlzdWFsaXplIG1lYW4gbGlmZSBleHBlY3RhbmN5IGJ5IHJhY2UgYW5kIGNpdHkNCmBgYHtyLCB3YXJuaW5nPUYsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCg0KZ2cgIDwtIGdncGxvdChsaWZlX2V4cC5kZi5vcmRlcmVkKSArIGFlcyh4PXJlb3JkZXIoUmFjZS4uRXRobmljaXR5LCBNZWFuX0xpZmVfRXhwX2J5X1JhY2VfYW5kX1BsYWNlKSAseT1NZWFuX0xpZmVfRXhwX2J5X1JhY2VfYW5kX1BsYWNlLCBmaWxsID0gUmFjZS4uRXRobmljaXR5KSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArIGZhY2V0X3dyYXAoflBsYWNlKSArIGxhYnModGl0bGUgPSAiTGlmZSBFeHBlY3RhbmN5IGJ5IFJhY2UgYW5kIENpdHkiKSArIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSkNCg0KZ2dwX2J1aWxkIDwtIHBsb3RseV9idWlsZChnZykNCmdncF9idWlsZCRsYXlvdXQkaGVpZ2h0ID0gMTAwMA0KZ2dwX2J1aWxkJGxheW91dCR3aWR0aCA9IDEwMDANCg0KZ2dwX2J1aWxkDQpgYGANCj4NCkFib3ZlIGlzIGFuIGludGVyYWN0aXZlIHBsb3Qgc2hvd2luZyB0aGUgbWVhbiBsaWZlIGV4cGVjdGFuY3kgZm9yIGNpdGllcyBieSByYWNlLg0KDQoNClZpc3VhbGl6ZSBtZWRpYW4gaG91c2Vob2xkIGluY29tZSBieSBjaXR5DQpgYGB7ciwgd2FybmluZz1GLCBlcnJvcj1GLCBtZXNzYWdlPUZ9DQppbmNvbWUgPC0gc3Vic2V0KGRhdGEsIEluZGljYXRvciA9PSAiTWVkaWFuIEhvdXNlaG9sZCBJbmNvbWUgKERvbGxhcnMpIiwgc2VsZWN0ID0gY29sbmFtZXMoZGF0YSkpDQppbmNvbWVfdmFsIDwtIGFnZ3JlZ2F0ZShWYWx1ZSB+IFBsYWNlLCBpbmNvbWUsIG1lYW4pDQpnZ3Bsb3QoaW5jb21lX3ZhbCwgYWVzKHg9cmVvcmRlcihQbGFjZSwgVmFsdWUpLCB5PVZhbHVlKSkgKyBnZW9tX2NvbCgpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZSA9ICJNZWRpYW4gSG91c2Vob2xkIEluY29tZSAoRG9sbGFycykiLCB4PSJDaXR5IikNCmBgYA0KDQo+DQpTYW4gSm9zZSwgQ0EgaGFzIHRoZSBoaWdoZXN0IE1lZGlhbiBIb3VzZWhvbGQgSW5jb21lLCB3aGlsZSBEZXRyb2l0LCBNSSBoYXMgdGhlIGxvd2VzdC4NCg0KVmlzdWFsaXplIE1vcnRhbGl0eSBSYXRlIGJ5IGNpdHkgYW5kIHJhY2UNCmBgYHtyLCB3YXJuaW5nPUYsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCmFsbF9jYXVzZSA8LSBzdWJzZXQoZGF0YSwgSW5kaWNhdG9yID09ICJBbGwtQ2F1c2UgTW9ydGFsaXR5IFJhdGUgKEFnZS1BZGp1c3RlZDsgUGVyIDEwMCwwMDAgcGVvcGxlKSIsIHNlbGVjdCA9IGNvbG5hbWVzKGRhdGEpKQ0KYWxsX2NhdXNlMiA8LSBhZ2dyZWdhdGUoIFZhbHVlIH4gUGxhY2UsIGFsbF9jYXVzZSwgbWVhbiApDQpnZ3Bsb3QoYWxsX2NhdXNlMiwgYWVzKHg9cmVvcmRlcihQbGFjZSwgVmFsdWUpLCB5PVZhbHVlKSkgKyBnZW9tX2NvbCgpICsgY29vcmRfZmxpcCgpICsgbGFicyh0aXRsZSA9ICJBbGwtQ2F1c2UgTW9ydGFsaXR5IFJhdGUgKEFnZS1BZGp1c3RlZDsgUGVyIDEwMCwwMDAgcGVvcGxlKSIsIHg9IkNpdHkiKQ0KYGBgDQo+DQpDbGV2ZWxhbmQsIE9IIGhhcyB0aGUgbG93ZXN0IEFsbC1DYXVzZSBNb3J0YWxpdHkgUmF0ZSwgd2hpbGUgUGhvZW5peCwgQVogaGFzIHRoZSBsb3dlc3QuDQoNClZpc3VhbGl6ZSBwZXJjZW50IG9mIGFkdWx0cyB3aG8gbWVldCBQaHlzaWNhbCBBY3Rpdml0eSBSZXF1aXJlbWVudHMNCmBgYHtyLCB3YXJuaW5nPUYsIGVycm9yPUYsIG1lc3NhZ2U9Rn0NCnBhIDwtIHN1YnNldChkYXRhLCBJbmRpY2F0b3IgPT0gIlBlcmNlbnQgb2YgQWR1bHRzIFdobyBNZWV0IENEQy1SZWNvbW1lbmRlZCBQaHlzaWNhbCBBY3Rpdml0eSBMZXZlbHMiLCBzZWxlY3QgPSBjb2xuYW1lcyhkYXRhKSkNCnBhX2RhdGEgPC0gYWdncmVnYXRlKCBWYWx1ZSB+IFBsYWNlLCBwYSwgbWVhbikNCmdncGxvdChwYV9kYXRhLCBhZXMoeD1yZW9yZGVyKFBsYWNlLCBWYWx1ZSksIHk9VmFsdWUpKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlID0gIlBlcmNlbnQgb2YgQWR1bHRzIFdobyBNZWV0IENEQy1SZWNvbW1lbmRlZCBQaHlzaWNhbCBBY3Rpdml0eSBMZXZlbHMiLCB4PSJDaXR5IikNCmBgYA0KPg0KRGVudmVyLCBDTyBoYXMgdGhlIGhpZ2hlc3QgcGVyY2VudGFnZSBvZiBhZHVsdHMgd2hvIG1lZXQgQ0RDLXJlY29tbWVuZCBwaHlzaWNhbCBhY3Rpdml0eSBsZXZlbHMsIHdoaWxlIERldHJvaXQsIE1JIGhhcyB0aGUgbG93ZXN0Lg0KDQpJbnRlcmVzdGluZyBwbG90cw0KYGBge3IsIHdhcm5pbmc9RiwgZXJyb3I9RiwgbWVzc2FnZT1GfQ0KZmEgPC0gc3Vic2V0KGRhdGEsIEluZGljYXRvciA9PSAiRmlyZWFybSBSZWxhdGVkIE1vcnRhbGl0eSBSYXRlIChBZ2UtQWRqdXN0ZWQ7IFBlciAxMDAsMDAwIHBlb3BsZSkiLCBzZWxlY3QgPSBjb2xuYW1lcyhkYXRhKSkNCmZhX2RhdGEgPC0gYWdncmVnYXRlKCBWYWx1ZSB+IFBsYWNlLCBmYSwgbWVhbikNCmdncGxvdChmYV9kYXRhLCBhZXMoeD1yZW9yZGVyKFBsYWNlLCBWYWx1ZSksIHk9VmFsdWUpKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlID0gIkZpcmVhcm0gUmVsYXRlZCBNb3J0YWxpdHkgUmF0ZSAoQWdlLUFkanVzdGVkOyBQZXIgMTAwLDAwMCBwZW9wbGUpIikNCmBgYA0KPg0KRGV0cm9pdCwgTUkgaGFzIHRoZSBoaWdoZXN0IGZpcmVhcm0tcmVsYXRlZCBtb3J0YWxpdHkgcmF0ZSwgd2hpbGUgTmV3IFlvcmssIE5ZIGhhcyB0aGUgbG93ZXN0Lg0KDQpgYGB7ciwgd2FybmluZz1GLCBlcnJvcj1GLCBtZXNzYWdlPUZ9DQpociA8LSBzdWJzZXQoZGF0YSwgSW5kaWNhdG9yID09ICJIb21pY2lkZSBSYXRlIChBZ2UtQWRqdXN0ZWQ7IFBlciAxMDAsMDAwIHBlb3BsZSkiLCBzZWxlY3QgPSBjb2xuYW1lcyhkYXRhKSkNCmhyX2RhdGEgPC0gYWdncmVnYXRlKCBWYWx1ZSB+IFBsYWNlLCBociwgbWVhbikNCmdncGxvdChocl9kYXRhLCBhZXMoeD1yZW9yZGVyKFBsYWNlLCBWYWx1ZSksIHk9VmFsdWUpKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlID0gIkhvbWljaWRlIFJhdGUgKEFnZS1BZGp1c3RlZDsgUGVyIDEwMCwwMDAgcGVvcGxlKSIsIHg9IkNpdHkiKQ0KYGBgDQo+DQpEZXRyb2l0LCBNSSBoYXMgdGhlIGhpZ2hlc3QgaG9taWNpZGUgcmF0ZSwgd2hpbGUgU2FuIERpZWdvLCBDQSBoYXMgdGhlIGxvd2VzdA0KDQpgYGB7ciwgd2FybmluZz1GLCBlcnJvcj1GLCBtZXNzYWdlPUZ9DQpzciA8LSBzdWJzZXQoZGF0YSwgSW5kaWNhdG9yID09ICJTdWljaWRlIFJhdGUgIChBZ2UtQWRqdXN0ZWQ7IFBlciAxMDAsMDAwIHBlb3BsZSkiLCBzZWxlY3QgPSBjb2xuYW1lcyhkYXRhKSkNCnNyX2RhdGEgPC0gYWdncmVnYXRlKCBWYWx1ZSB+IFBsYWNlLCBzciwgbWVhbikNCmdncGxvdChzcl9kYXRhLCBhZXMoeD1yZW9yZGVyKFBsYWNlLCBWYWx1ZSksIHk9VmFsdWUpKSArIGdlb21fY29sKCkgKyBjb29yZF9mbGlwKCkgKyBsYWJzKHRpdGxlID0gIlN1aWNpZGUgUmF0ZSAgKEFnZS1BZGp1c3RlZDsgUGVyIDEwMCwwMDAgcGVvcGxlKSIpDQpgYGANCj4NClNhY3JhbWVudG8sIENBIGhhcyB0aGUgaGlnaGVzdCBzdWljaWRlIHJhdGUsIHdoaWxlIExvcyBBbmdlbGVzLCBDQSBoYXMgdGhlIGxvd2VzdC4=